#include "utils/Config.h"
#include "utils/Json.h"
#include "core/Logger.h"

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <stdexcept>
#include <utility>

namespace ems {
namespace utils {

Config& Config::getInstance() {
    static Config instance;
    return instance;
}

bool Config::load(const std::string& configPath) {
    try {
        configRoot_ = configPath;
        bool ok = loadDevices(configPath) && 
                  loadMqtt(configPath) && 
                  loadModbus(configPath) &&
                  loadSystem(configPath);

        // 时段电价不是核心启动条件，加载失败时仅记录日志
        loadTimeOfUsePrices(configPath);

        return ok;
    } catch (const std::exception& e) {
        LOG_ERROR("Failed to load configuration: " << e.what());
        return false;
    }
}

namespace {

bool replaceJsonStringField(std::string& content, const std::string& key, const std::string& value) {
    const std::string pattern = "\"" + key + "\"";
    size_t keyPos = content.find(pattern);
    if (keyPos == std::string::npos) {
        return false;
    }
    size_t colonPos = content.find(':', keyPos);
    if (colonPos == std::string::npos) {
        return false;
    }
    size_t firstQuote = content.find('"', colonPos);
    if (firstQuote == std::string::npos) {
        return false;
    }
    size_t secondQuote = content.find('"', firstQuote + 1);
    if (secondQuote == std::string::npos) {
        return false;
    }
    content.replace(firstQuote + 1, secondQuote - firstQuote - 1, value);
    return true;
}

int parseMinutesFromTimeString(const std::string& value) {
    if (value.empty()) {
        return -1;
    }
    int hour = 0;
    int minute = 0;
    if (std::sscanf(value.c_str(), "%d:%d", &hour, &minute) != 2) {
        return -1;
    }
    if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60) {
        return -1;
    }
    return hour * 60 + minute;
}

std::string toLowerAscii(std::string text) {
    std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) {
        return static_cast<char>(std::tolower(c));
    });
    return text;
}

void loadPeriodRanges(const Json& parent,
                      const std::string& keyZh,
                      const std::string& keyEn,
                      std::vector<TimeRange>& out)
{
    Json node = parent.getObject(keyZh);
    if (node.isNull()) {
        node = parent.getObject(keyEn);
    }
    if (node.isNull()) {
        return;
    }

    auto ranges = node.getArray("时段");
    if (ranges.empty()) {
        ranges = node.getArray("ranges");
    }

    for (const auto& rangeJson : ranges) {
        std::string startStr = rangeJson.getString("开始", rangeJson.getString("start"));
        std::string endStr = rangeJson.getString("结束", rangeJson.getString("end"));

        const int startMinute = parseMinutesFromTimeString(startStr);
        const int endMinute = parseMinutesFromTimeString(endStr);

        if (startMinute < 0 || endMinute < 0) {
            LOG_WARN("Invalid time range in time_of_use_prices: " << startStr << " - " << endStr);
            continue;
        }

        TimeRange range;
        range.startMinute = startMinute;
        range.endMinute = endMinute;
        const bool fullDay = (startMinute == 0 && endMinute == 0);
        range.crossesMidnight = fullDay || endMinute <= startMinute;
        out.push_back(range);
    }
}

} // namespace

bool Config::saveMqttIdentity(const std::string& deviceSn, const std::string& clientId) {
    if (configRoot_.empty()) {
        LOG_MQTT_ERROR("Config root path is empty, cannot persist MQTT identity");
        return false;
    }

    const std::string mqttFile = configRoot_ + "/mqtt.conf";
    std::ifstream in(mqttFile);
    if (!in.is_open()) {
        LOG_MQTT_ERROR("Failed to open MQTT config file for update: " << mqttFile);
        return false;
    }

    std::string content((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
    in.close();

    bool updated = false;
    if (!deviceSn.empty()) {
        if (replaceJsonStringField(content, "device_sn", deviceSn)) {
            mqtt_.deviceSn = deviceSn;
            updated = true;
        }
    }
    if (!clientId.empty()) {
        if (replaceJsonStringField(content, "client_id", clientId)) {
            mqtt_.clientId = clientId;
            updated = true;
        }
    }

    if (!updated) {
        return false;
    }

    std::ofstream out(mqttFile);
    if (!out.is_open()) {
        LOG_MQTT_ERROR("Failed to write MQTT config file: " << mqttFile);
        return false;
    }
    out << content;
    out.close();

    if (clientId.empty()) {
        mqtt_.clientId = mqtt_.deviceSn;
    }

    return true;
}

bool Config::loadDevices(const std::string& configPath) {
    std::string devicesFile = configPath + "/devices.json";
    std::ifstream file(devicesFile);
    if (!file.is_open()) {
        LOG_ERROR("Cannot open devices config file: " << devicesFile);
        return false;
    }
    
    Json json;
    if (!json.parse(file)) {
        LOG_ERROR("Failed to parse devices config file");
        return false;
    }
    
    devices_.clear();
    
    auto devicesArray = json.getArray("devices");
    for (const auto& deviceJson : devicesArray) {
        DeviceConfig device;
        device.id = deviceJson.getString("id");
        if (device.id.empty()) {
            double idNum = deviceJson.getDouble("id", std::numeric_limits<double>::quiet_NaN());
            if (!std::isnan(idNum)) {
                long long idInt = static_cast<long long>(idNum);
                device.id = std::to_string(idInt);
            }
        }
        device.name = deviceJson.getString("name");
        device.type = deviceJson.getString("type");
        device.protocol = deviceJson.getString("protocol");

        auto connectionJson = deviceJson.getObject("connection");
        for (const auto& pair : connectionJson.getMap()) {
            device.connection[pair.first] = pair.second.asString();
        }

        device.protocolConfig = deviceJson.getString("protocol_config");
        device.pollInterval = deviceJson.getInt("poll_interval", 5000);
        device.uploadInterval = deviceJson.getInt("upload_interval", -1);
        device.enabled = deviceJson.getBool("enabled", true);

        // MQTT sync related fields
        device.deviceType = deviceJson.getInt("deviceType", 10100);  // 默认设备类型
        device.deviceSn = deviceJson.getString("deviceSn", "");      // 设备序列号
        device.onlineState = deviceJson.getInt("onlineState", 0);    // 默认离线状态
        device.subDeviceCount = deviceJson.getInt("subDeviceCount", 0); // 子设备数量

        // 解析物模型 models（对象形式）
        {
            auto modelsObj = deviceJson.getObject("models");
            for (const auto& kv : modelsObj.getMap()) {
                DeviceConfig::ModelConfig mc;
                const auto& node = kv.second;
                if (node.isObject()) {
                    mc.enabled = node.getBool("enabled", true);
                    mc.uploadIntervalSec = node.getInt("upload_interval", -1);
                    std::string modelDeviceSn = node.getString("deviceSn");
                    if (modelDeviceSn.empty()) {
                        modelDeviceSn = node.getString("device_sn");
                    }
                    mc.deviceSn = modelDeviceSn;
                    int modelDeviceType = node.getInt("deviceType", 0);
                    if (modelDeviceType == 0) {
                        modelDeviceType = node.getInt("device_type", 0);
                    }
                    mc.deviceType = modelDeviceType;
                    std::string modelName = node.getString("deviceName");
                    if (modelName.empty()) {
                        modelName = node.getString("name");
                    }
                    mc.deviceName = modelName;
                    mc.index = node.getInt("index", 1);
                } else if (node.isBool()) {
                    // 简化容错：布尔直接表示启用
                    mc.enabled = node.asBool();
                    mc.uploadIntervalSec = -1;
                    mc.index = 1;
                } else {
                    // 其他情况：若为数字则视为上报间隔（秒）
                    mc.enabled = true;
                    mc.uploadIntervalSec = node.isNumber() ? node.asInt() : -1;
                    mc.index = 1;
                }
                device.models[kv.first] = mc;
            }
        }

        devices_.push_back(device);
    }
    
    LOG_INFO("Loaded " << devices_.size() << " device configurations");
    return true;
}

bool Config::loadMqtt(const std::string& configPath) {
    std::string mqttFile = configPath + "/mqtt.conf";
    std::ifstream file(mqttFile);
    if (!file.is_open()) {
        LOG_MQTT_ERROR("Cannot open MQTT config file: " << mqttFile);
        return false;
    }
    
    Json json;
    if (!json.parse(file)) {
        LOG_MQTT_ERROR("Failed to parse MQTT config file");
        return false;
    }
    
    auto brokerJson = json.getObject("broker");
    mqtt_.host = brokerJson.getString("host");
    mqtt_.port = brokerJson.getInt("port", 1883);
    mqtt_.useTls = brokerJson.getBool("use_tls", false);
    mqtt_.caFile = brokerJson.getString("ca_file");
    mqtt_.certFile = brokerJson.getString("cert_file");
    mqtt_.keyFile = brokerJson.getString("key_file");
    
    auto clientJson = json.getObject("client");
    mqtt_.clientId = clientJson.getString("client_id");
    mqtt_.deviceSn = clientJson.getString("device_sn");
    mqtt_.deviceType = clientJson.getString("device_type");
    mqtt_.username = clientJson.getString("username");
    mqtt_.password = clientJson.getString("password");
    mqtt_.keepAlive = clientJson.getInt("keep_alive", 300);
    mqtt_.cleanSession = clientJson.getBool("clean_session", true);
    mqtt_.qos = clientJson.getInt("qos", 1);
    mqtt_.retained = clientJson.getBool("retained", false);
    
    auto topicsJson = json.getObject("topics");
    for (const auto& pair : topicsJson.getMap()) {
        mqtt_.topics[pair.first] = pair.second.asString();
    }

    auto securityJson = json.getObject("security");
    mqtt_.signMethod = securityJson.getString("sign_method");
    mqtt_.deviceTypeSecret = securityJson.getString("device_type_secret");
    if (mqtt_.clientId.empty() && !mqtt_.deviceSn.empty()) {
        mqtt_.clientId = mqtt_.deviceSn;
    }

    LOG_INFO("Loaded MQTT configuration");
    return true;
}

bool Config::loadModbus(const std::string& configPath) {
    std::string modbusFile = configPath + "/modbus.conf";
    std::ifstream file(modbusFile);
    if (!file.is_open()) {
        LOG_ERROR("Cannot open Modbus config file: " << modbusFile);
        return false;
    }
    
    Json json;
    if (!json.parse(file)) {
        LOG_ERROR("Failed to parse Modbus config file");
        return false;
    }
    
    // Modbus TCP配置
    auto tcpJson = json.getObject("modbus_tcp");
    modbus_.defaultTcpTimeout = tcpJson.getInt("default_timeout", 3000);
    modbus_.defaultTcpPort = tcpJson.getInt("default_port", 502);
    modbus_.tcpRetryCount = tcpJson.getInt("connection_retry_count", 3);
    modbus_.tcpRetryInterval = tcpJson.getInt("connection_retry_interval", 1000);
    modbus_.tcpReadTimeout = tcpJson.getInt("read_timeout", 5000);
    modbus_.tcpWriteTimeout = tcpJson.getInt("write_timeout", 3000);
    modbus_.maxConcurrentConnections = tcpJson.getInt("max_concurrent_connections", 10);
    
    // Modbus RTU配置
    auto rtuJson = json.getObject("modbus_rtu");
    modbus_.defaultBaudrate = rtuJson.getInt("default_baudrate", 9600);
    modbus_.defaultParity = rtuJson.getString("default_parity", "N");
    modbus_.defaultDataBits = rtuJson.getInt("default_data_bits", 8);
    modbus_.defaultStopBits = rtuJson.getInt("default_stop_bits", 1);
    modbus_.rtuTimeout = rtuJson.getInt("default_timeout", 3000);
    modbus_.rtuRetryCount = rtuJson.getInt("connection_retry_count", 3);
    modbus_.rtuRetryInterval = rtuJson.getInt("connection_retry_interval", 1000);
    modbus_.rtuReadTimeout = rtuJson.getInt("read_timeout", 5000);
    modbus_.rtuWriteTimeout = rtuJson.getInt("write_timeout", 3000);
    modbus_.frameDelay = rtuJson.getInt("frame_delay", 20);
    modbus_.responseTimeout = rtuJson.getInt("response_timeout", 1000);
    
    // 通用配置
    auto generalJson = json.getObject("general");
    modbus_.pollInterval = generalJson.getInt("poll_interval", 5000);
    modbus_.errorThreshold = generalJson.getInt("error_threshold", 5);
    modbus_.reconnectDelay = generalJson.getInt("reconnect_delay", 5000);
    modbus_.maxRegisterCount = generalJson.getInt("max_register_count", 125);
    modbus_.enableLogging = generalJson.getBool("enable_logging", true);
    modbus_.logLevel = generalJson.getString("log_level", "INFO");
    
    LOG_INFO("Loaded Modbus configuration");
    return true;
}

bool Config::loadSystem(const std::string& configPath) {
    std::string systemFile = configPath + "/system.json";
    std::ifstream file(systemFile);
    if (!file.is_open()) {
        LOG_ERROR("Cannot open system config file: " << systemFile);
        return false;
    }
    
    Json json;
    if (!json.parse(file)) {
        LOG_ERROR("Failed to parse system config file");
        return false;
    }
    
    auto systemJson = json.getObject("system");
    system_.logLevel = systemJson.getString("log_level", "INFO");
    system_.logFile = systemJson.getString("log_file", "/var/log/ems-cpp/application.log");
    system_.maxLogSize = systemJson.getString("max_log_size", "100MB");
    system_.dataCacheSize = systemJson.getInt("data_cache_size", 1000);
    system_.threadPoolSize = systemJson.getInt("thread_pool_size", 4);
    
    auto collectionJson = json.getObject("collection");
    system_.retryTimes = collectionJson.getInt("retry_times", 3);
    system_.retryInterval = collectionJson.getInt("retry_interval", 1000);
    system_.batchSize = collectionJson.getInt("batch_size", 10);
    system_.queueMaxSize = collectionJson.getInt("queue_max_size", 1000);
    
    auto uploadJson = json.getObject("upload");
    system_.batchUpload = uploadJson.getBool("batch_upload", true);
    system_.uploadInterval = uploadJson.getInt("upload_interval", 10000);

    dispatch_ = DispatchConfig{};
    auto dispatchJson = json.getObject("dispatch");
    if (!dispatchJson.isNull()) {
        dispatch_.enabled = dispatchJson.getBool("enabled", dispatch_.enabled);
        dispatch_.pvFeedPrice = dispatchJson.getDouble("pv_feed_price", dispatch_.pvFeedPrice);
        dispatch_.storageChargePrice = dispatchJson.getDouble("storage_charge_price", dispatch_.storageChargePrice);
        dispatch_.gridPricePeak = dispatchJson.getDouble("grid_price_peak", dispatch_.gridPricePeak);
        dispatch_.criticalLoadKw = dispatchJson.getDouble("critical_load_kw", dispatch_.criticalLoadKw);
    }

    // Override dispatch enabled from persistent flag if present
    std::ifstream flagFile(configPath + "/dispatch.enabled");
    if (flagFile.is_open()) {
        std::string value;
        flagFile >> value;
        if (!value.empty()) {
            std::string lower;
            lower.reserve(value.size());
            std::transform(value.begin(), value.end(), std::back_inserter(lower), [](unsigned char c){ return static_cast<char>(std::tolower(c)); });
            dispatch_.enabled = !(lower == "0" || lower == "false" || lower == "off");
        }
    }
    
    LOG_INFO("Loaded system configuration");
    return true;
}

bool Config::loadTimeOfUsePrices(const std::string& configPath) {
    timeOfUsePrices_ = TimeOfUseTable{};

    const std::string touFile = configPath + "/time_of_use_prices.json";
    std::ifstream file(touFile);
    if (!file.is_open()) {
        LOG_INFO("Time-of-use price file not found: " << touFile);
        return false;
    }

    Json json;
    if (!json.parse(file)) {
        LOG_ERROR("Failed to parse time-of-use price file: " << touFile);
        return false;
    }

    TimeOfUseTable table;
    table.unit = json.getString("单位", json.getString("unit", "CNY/kWh"));

    Json periods = json.getObject("时段定义");
    if (periods.isNull()) {
        periods = json.getObject("periods");
    }

    loadPeriodRanges(periods, "高峰时段", "peak", table.schedule.peak);
    loadPeriodRanges(periods, "平时段", "flat", table.schedule.flat);
    loadPeriodRanges(periods, "低谷时段", "valley", table.schedule.valley);

    auto monthlyArray = json.getArray("月度电价");
    if (monthlyArray.empty()) {
        monthlyArray = json.getArray("monthly_prices");
    }

    if (monthlyArray.empty()) {
        LOG_WARN("time_of_use_prices.json does not contain monthly tariff data");
        return false;
    }

    const bool unitIsCent = table.unit.find("分") != std::string::npos;
    const std::string unitLower = toLowerAscii(table.unit);
    const bool unitIsCentEnglish = unitLower.find("cent") != std::string::npos;
    const double unitScale = (unitIsCent || unitIsCentEnglish) ? 0.01 : 1.0;

    for (const auto& monthJson : monthlyArray) {
        MonthlyTariff tariff;
        tariff.month = monthJson.getInt("月份", monthJson.getInt("month", 0));
        if (tariff.month <= 0 || tariff.month > 12) {
            LOG_WARN("Invalid month in time_of_use_prices.json: " << tariff.month);
            continue;
        }

        auto readPrice = [&](const std::string& zhKey, const std::string& enKey) -> double {
            const double value = monthJson.getDouble(zhKey, monthJson.getDouble(enKey, std::numeric_limits<double>::quiet_NaN()));
            if (std::isnan(value)) {
                return 0.0;
            }
            return value * unitScale;
        };

        tariff.peak = readPrice("高峰时段", "peak");
        tariff.flat = readPrice("平时段", "flat");
        tariff.valley = readPrice("低谷时段", "valley");

        table.monthly[tariff.month] = tariff;
    }

    if (table.monthly.empty()) {
        LOG_WARN("No valid time-of-use monthly tariff entries were loaded");
        return false;
    }

    table.loaded = true;
    timeOfUsePrices_ = std::move(table);
    LOG_INFO("Loaded time-of-use price table");
    return true;
}

std::string Config::getString(const std::string& key, const std::string& defaultValue) const {
    auto it = additionalConfig_.find(key);
    return (it != additionalConfig_.end()) ? it->second : defaultValue;
}

int Config::getInt(const std::string& key, int defaultValue) const {
    auto it = additionalConfig_.find(key);
    if (it != additionalConfig_.end()) {
        try {
            return std::stoi(it->second);
        } catch (const std::exception&) {
            return defaultValue;
        }
    }
    return defaultValue;
}

bool Config::getBool(const std::string& key, bool defaultValue) const {
    auto it = additionalConfig_.find(key);
    if (it != additionalConfig_.end()) {
        return it->second == "true" || it->second == "1";
    }
    return defaultValue;
}

bool Config::resolveTimeOfUseContext(std::int64_t timestampSeconds,
                                     MonthlyTariff& tariff,
                                     TimeOfUseTier& tier,
                                     int& minuteOfDay) const {
    if (!timeOfUsePrices_.loaded) {
        LOG_WARN("Time-of-use price table not loaded, returning fallback");
        return false;
    }

    std::time_t ts = static_cast<std::time_t>(timestampSeconds);
    std::tm localTime{};
#if defined(_WIN32)
    if (localtime_s(&localTime, &ts) != 0) {
        LOG_WARN("localtime_s failed for timestamp: " << timestampSeconds);
        return false;
    }
#else
    if (localtime_r(&ts, &localTime) == nullptr) {
        LOG_WARN("localtime_r failed for timestamp: " << timestampSeconds);
        return false;
    }
#endif

    const int month = localTime.tm_mon + 1;
    minuteOfDay = localTime.tm_hour * 60 + localTime.tm_min;

    auto monthIt = timeOfUsePrices_.monthly.find(month);
    if (monthIt == timeOfUsePrices_.monthly.end()) {
        LOG_WARN("No time-of-use price entry for month " << month << ", defaulting to 0");
        return false;
    }

    tariff = monthIt->second;

    auto contains = [&](const std::vector<TimeRange>& ranges) {
        for (const auto& range : ranges) {
            if (range.contains(minuteOfDay)) {
                return true;
            }
        }
        return false;
    };

    tier = TimeOfUseTier::Flat;
    if (!timeOfUsePrices_.schedule.peak.empty() && contains(timeOfUsePrices_.schedule.peak)) {
        tier = TimeOfUseTier::Peak;
    } else if (!timeOfUsePrices_.schedule.valley.empty() && contains(timeOfUsePrices_.schedule.valley)) {
        tier = TimeOfUseTier::Valley;
    } else if (!timeOfUsePrices_.schedule.flat.empty() && contains(timeOfUsePrices_.schedule.flat)) {
        tier = TimeOfUseTier::Flat;
    }

    return true;
}

double Config::getTimeOfUsePrice(std::int64_t timestampSeconds) const {
    MonthlyTariff tariff;
    TimeOfUseTier tier = TimeOfUseTier::Flat;
    int minuteOfDay = 0;
    if (!resolveTimeOfUseContext(timestampSeconds, tariff, tier, minuteOfDay)) {
        return 0.0;
    }

    auto resolvePrice = [&](TimeOfUseTier selectedTier) -> double {
        switch (selectedTier) {
            case TimeOfUseTier::Peak:
                if (tariff.peak > 0.0) return tariff.peak;
                break;
            case TimeOfUseTier::Valley:
                if (tariff.valley > 0.0) return tariff.valley;
                break;
            case TimeOfUseTier::Flat:
            default:
                if (tariff.flat > 0.0) return tariff.flat;
                break;
        }

        if (tariff.flat > 0.0) return tariff.flat;
        if (tariff.peak > 0.0) return tariff.peak;
        if (tariff.valley > 0.0) return tariff.valley;
        return 0.0;
    };

    return resolvePrice(tier);
}

double Config::getTimeOfUsePeakPrice(std::int64_t timestampSeconds) const {
    MonthlyTariff tariff;
    TimeOfUseTier tier = TimeOfUseTier::Flat;
    int minuteOfDay = 0;
    if (!resolveTimeOfUseContext(timestampSeconds, tariff, tier, minuteOfDay)) {
        return 0.0;
    }

    if (tariff.peak > 0.0) {
        return tariff.peak;
    }

    double fallback = std::max({tariff.flat, tariff.valley, tariff.peak});
    return fallback > 0.0 ? fallback : 0.0;
}

bool Config::isTimeOfUsePeakPeriod(std::int64_t timestampSeconds) const {
    MonthlyTariff tariff;
    TimeOfUseTier tier = TimeOfUseTier::Flat;
    int minuteOfDay = 0;
    if (!resolveTimeOfUseContext(timestampSeconds, tariff, tier, minuteOfDay)) {
        return false;
    }
    return tier == TimeOfUseTier::Peak;
}

void Config::setDispatchEnabled(bool enabled) {
    dispatch_.enabled = enabled;

    if (configRoot_.empty()) {
        LOG_WARN("Config root not set, skip persisting dispatch enabled flag");
        return;
    }

    const std::string flagFile = configRoot_ + "/dispatch.enabled";
    std::ofstream out(flagFile, std::ios::out | std::ios::binary | std::ios::trunc);
    if (!out.is_open()) {
        LOG_ERROR("Failed to open dispatch enabled flag file: " << flagFile);
        return;
    }
    out << (enabled ? "1" : "0");
    out.close();
}

bool Config::reloadDevices() {
    if (configRoot_.empty()) {
        LOG_ERROR("Config root not set, cannot reload devices");
        return false;
    }
    return loadDevices(configRoot_);
}

}  // namespace utils
}  // namespace ems
